use std::borrow::Borrow;
use std::error::Error;
use std::fs::File;
use std::io::{Bytes, stderr, stdin, stdout, Write};
use crate::lincni::{
    cmd::{CmdArgs, Dispatcher},
    convertion::{types040::{Result040}},
    plugin::PluginInfo,
    types::{ErrorResponse, NetConfig,ErrorType::{ErrUnknown}},
};
#[cfg(target_os = "linux")]
use crate::sys::proc::new_proc;
#[cfg(target_os = "windows")]
use crate::sys::win::new_proc;
use serde::{Serialize,Deserialize};
use crate::{Client, UnixRequest};
use crate::types::pod::Pod;
use crate::types::response::UnixResponse;

pub trait Runner {
    fn add(&mut self,args:&CmdArgs,logger:&mut File) -> Result<(),ErrorResponse> ;
    fn del(&mut self,args:&CmdArgs,logger:&mut File) -> Result<(),ErrorResponse> ;
    fn check(&mut self,args:&CmdArgs,logger:&mut File) -> Result<(),ErrorResponse> ;
}


pub struct Run {
    client:Client,
}


impl Runner for Run {
    fn add(&mut self,args:&CmdArgs,logger:&mut File) -> Result<(),ErrorResponse>  {
        #[derive(Serialize,Deserialize,Default,Debug)]
        struct conf {
            #[serde(skip_deserializing,skip_serializing)]
            netconf:NetConfig,
            #[serde(default)]
            bridge: String,
            #[serde(default)]
            subnet: String,
        }
        let res = serde_json::from_slice::<conf>(args.stdin_data.as_slice());
        if let Err(e) = res.borrow() {
            return Err(ErrorResponse::new(ErrUnknown,e.to_string(),"".to_string()));
        }
        let mut c = res.unwrap();
    
        let res = serde_json::from_slice::<NetConfig>(args.stdin_data.as_slice());
        if let Err(e) = res.borrow() {
            return Err(ErrorResponse::new(ErrUnknown,e.to_string(),"".to_string()));
        }
        c.netconf = res.unwrap();

        let ve = serde_json::to_vec(&c).unwrap();
        logger.write(ve.as_slice()).unwrap();
        logger.write(&['\n' as u8]).unwrap();

        let v = serde_json::to_value(&c.netconf.prev_result.unwrap()).unwrap();
        let result:Result040 = serde_json::from_value(v).unwrap();
        if let Err(e) = result.print_result() {
            return Err(ErrorResponse::new(ErrUnknown,e.to_string(),"".to_string()));
        }

        let arg_map = args.parse_args();
        let name = arg_map.get("K8S_POD_NAME").unwrap().clone();
        let namespace = arg_map.get("K8S_POD_NAMESPACE").unwrap().clone();
        let request = UnixRequest{
            name,
            namespace,
            typ: "pod".to_string()
        };
        let res =  self.client.do_request(request.to_vec());
        if let Err(e) = res.borrow() {
            return Err(ErrorResponse::new(ErrUnknown,e.to_string(),"".to_string()));
        }
        let response = res.unwrap();
        let res = serde_json::from_str(response.data.as_str());
        if let Err(e) = res.borrow() {
            return Err(ErrorResponse::new(ErrUnknown,e.to_string(),"".to_string()));
        }
        let pod:Pod = res.unwrap();
        let res = pod.metadata.annotations.get("mesh.cni.plugin");
        if res.is_none() || res.unwrap() != "cni" {
            return Ok(());
        }


        let r = new_proc(args.net_ns.clone(),logger);
        match r {
            Ok(_) => {logger.write(format!("add iptables ok\n").as_bytes());},
            Err(e) => {logger.write(format!("add iptables err:{}\n",e.to_string()).as_bytes());},
        }
    
        Ok(())
    }

    fn del(&mut self,args:&CmdArgs,logger:&mut File) -> Result<(),ErrorResponse>  {
        Ok(())
    }

    fn check(&mut self,args:&CmdArgs,logger:&mut File) -> Result<(),ErrorResponse>  {
        Ok(())
    }
}

impl Run {
    pub fn new() -> Self {
        let client = Client::default();
        let r = Run { client };
        
        r
    }

}